/*
 *    Copyright (c) 2014 Xiang Xu <xuxiang@mail.bnu.edu.cn>
 *
 *    Permission is hereby granted, free of charge, to any person
 *    obtaining a copy of this software and associated documentation
 *    files (the "Software"), to deal in the Software without
 *    restriction, including without limitation the rights to use,
 *    copy, modify, merge, publish, distribute, sublicense, and/or sell
 *    copies of the Software, and to permit persons to whom the
 *    Software is furnished to do so, subject to the following
 *    conditions:
 *
 *    The above copyright notice and this permission notice shall be
 *    included in all copies or substantial portions of the Software.
 *
 *    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 *    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 *    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 *    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *    OTHER DEALINGS IN THE SOFTWARE.
 */

#include "facade_grammar.h"
#include "rapidjson/reader.h"
#include "rapidjson/filestream.h"
#include "rapidjson/document.h"
#include "opencv2/opencv.hpp"

FacadeGrammar::FacadeGrammar()
{
    has_empty_symbol_ = false;
}

FacadeGrammar::~FacadeGrammar()
{

}

Status FacadeGrammar::ReadGrammar(std::string& configure_file)
{
    rapidjson::Document document;
    FILE* fp = fopen(configure_file.c_str(), "r");

    if(fp == NULL)
    {
        return Status::OpenFileFail();
    }

    rapidjson::FileStream is(fp);

    if(document.ParseStream<0>(is).HasParseError())
    {
        return Status("Parse file fail");
    }

    if(!document.IsObject())
    {
        return Status("Parse file fail");
    }

    // atom symbol
    if(document.HasMember("atom"))
    {
        atom_symbol_ = document["atom"].GetString();
    }
    else
    {
        return Status("Not specify \"atom\" option!");
    }

    // terminal symbols
    if(document.HasMember("terminal"))
    {
        const rapidjson::Value& terminals = document["terminal"];

        if(!terminals.IsArray())
            return Status("\"terminal\" option is not arrary!");

        for(int i = 0; i < terminals.Size(); ++i)
        {
            const rapidjson::Value& terminal_symbol = terminals[i];

            if(!terminal_symbol.IsObject())  // A terminal symbol can have a color
                return Status("Each terminal in \"terminal\" array should be a Object!");

            if(terminal_symbol.HasMember("name"))
            {
                std::string symbol_name = terminal_symbol["name"].GetString();

                if(symbol_name == "empty")
                    has_empty_symbol_ = true;

                terminal_symbol_.push_back(symbol_name);
            }
            else
            {
                return Status("Terminal Must specify \"name\" option!");
            }

            if(terminal_symbol.HasMember("color"))
            {
                std::vector<double> color;

                if(terminal_symbol["color"].Size() != 3)
                    return Status("\"color\" option is not arrary of size 3!");

                for(int j = 0; j < 3; ++j)
                {
                    color.push_back(terminal_symbol["color"][j].GetDouble());
                }

                symbol_colors_.push_back(color);

                std::pair<std::string, int> color_pair;
                color_pair.first = terminal_symbol_[i];
                color_pair.second = i;
                symbol_color_map_.insert(color_pair);
            }
        }
    }

    // unterminal symbols
    if(document.HasMember("unterminal"))
    {
        const rapidjson::Value& unterminals = document["unterminal"];

        if(!unterminals.IsArray())
            return Status("\"unterminal\" option is not arrary!");

        for(int i = 0; i < unterminals.Size(); ++i)
        {
            unterminal_symbol_.push_back(unterminals[i].GetString());
        }
    }

    // grammars
    if(document.HasMember("grammar"))
    {
        const rapidjson::Value& grammar = document["grammar"];

        if(!grammar.IsArray())
            return Status("\"grammar\" option is not arrary!");

        for(int i = 0; i < grammar.Size(); ++i)
        {
            const rapidjson::Value& grammar_object = grammar[i];

            if(!grammar_object.IsObject())
                return Status("grammar item is not object!");

            ShapeGrammar shape_grammar;

            if(grammar_object.HasMember("name"))
            {
                shape_grammar.name_ = grammar_object["name"].GetString();
                grammar_names_.push_back(grammar_object["name"].GetString());
            }
            else
            {
                return Status("\"name\" option is not specified for grammar object!");
            }

            if(grammar_object.HasMember("direction"))
                shape_grammar.apply_direction_ = grammar_object["direction"].GetInt();
            else
                return Status("\"direction\" option is not specified for grammar object!");

            if(grammar_object.HasMember("parent"))
                shape_grammar.left_hand_symbol_ = grammar_object["parent"].GetString();
            else
                return Status("\"parent\" option is not specified for grammar object!");

            if(grammar_object.HasMember("child1"))
                shape_grammar.right_hand_symbol1_ = grammar_object["child1"].GetString();
            else
                return Status("\"child1\" option is not specified for grammar object!");

            if(grammar_object.HasMember("child2"))
                shape_grammar.right_hand_symbol2_ = grammar_object["child2"].GetString();
            else
                return Status("\"child2\" option is not specified for grammar object!");

            if(grammar_object.HasMember("symbol_parameter"))
                shape_grammar.symbol_parameter_ = grammar_object["symbol_parameter"].GetString();
            else
                shape_grammar.symbol_parameter_ = "none";

            if(grammar_object.HasMember("value_parameter"))
            {
                for(int i = 0; i < grammar_object["value_parameter"].Size(); ++i)
                {
                    shape_grammar.value_parameter_.push_back(grammar_object["value_parameter"][i].GetInt());
                }
            }

            if(grammar_object.HasMember("parameter_range"))
            {
                shape_grammar.parameter_range_.push_back(grammar_object["parameter_range"][(rapidjson::SizeType)0].GetInt());
                shape_grammar.parameter_range_.push_back(grammar_object["parameter_range"][(rapidjson::SizeType)1].GetInt());
            }

            if(grammar_object.HasMember("exception"))
                shape_grammar.exception_symbol_ = grammar_object["exception"].GetString();
            else
                return Status("\"exception\" option is not specified for grammar object!");

            grammar_set_.push_back(shape_grammar);
        }
    }

    // symbol_apply_direction_
    for(size_t i = 0; i < grammar_set_.size(); ++i)
    {
        if(atom_symbol_ == grammar_set_[i].left_hand_symbol_)
        {
            std::pair<std::string, int> str_dir;
            str_dir.first = atom_symbol_;
            str_dir.second = grammar_set_[i].apply_direction_;
            symbol_apply_direction_.insert(str_dir);
        }
        else
        {
            for(size_t j = 0; j < unterminal_symbol_.size(); ++j)
            {
                if(unterminal_symbol_[j] == grammar_set_[i].left_hand_symbol_)
                {
                    std::pair<std::string, int> str_dir;
                    str_dir.first = unterminal_symbol_[j];
                    str_dir.second = grammar_set_[i].apply_direction_;
                    symbol_apply_direction_.insert(str_dir);
                }
            }
        }
    }

    // terminal symbol index
    for(size_t i = 0; i < terminal_symbol_.size(); ++i)
    {
        terminal_symbol_index_.insert(std::make_pair(terminal_symbol_[i], i));
    }

    return Status::Success();
}

std::vector<double> FacadeGrammar::get_symbol_color(std::string symbol)
{
    int index = symbol_color_map_.at(symbol);
    return symbol_colors_[index];
}
